#import "BWLayerView.h"
#import "BWLayer.h"
#import "BWLayerSet.h"
#import "BWFabric.h"

#import "BWTool.h"

@implementation BWLayerView


- (id) initWithFrame: (NSRect) frame
{
    if (self = [super initWithFrame: frame]) {
        zoomFactor = 1.0;
    } 

    return (self);

} // initWithFrame


- (void) dealloc
{
    NSNotificationCenter *center;

    center = [NSNotificationCenter defaultCenter];
    [center removeObserver: self];

    [super dealloc];
    
} // dealloc


- (void) adjustFrame
{
    NSRect frame = NSZeroRect;

    // for now assuming all layers are of uniform bounds

    if ([layers count] > 0) {
        BWLayer *layer = [layers layerAtIndex: 0];
        frame = [layer bounds];
    }

    frame.size.width *= zoomFactor;
    frame.size.height *= zoomFactor;

    [self setFrame: frame];
    [self setNeedsDisplay: YES];

} // adjustFrame


// !!! hack to get the panner to update when changes happen here
- (void) setNeedsDisplay: (BOOL) ook
{
    [super setNeedsDisplay: YES];

    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];

    [center postNotificationName: NSViewFrameDidChangeNotification
            object: self];

} // setNeedsDisplay
// !!! end hack


- (NSAffineTransform *) zoomTransform
{
    NSAffineTransform *transform = [NSAffineTransform transform];

    [transform translateXBy: 0.5
               yBy: 0.5];
    [transform scaleXBy: zoomFactor  yBy: zoomFactor];

    return (transform);

} // zoomTransform


- (void) drawRect: (NSRect) rect
{
    NSSize halfUnit = { 0.5, 0.5 };
    halfUnit = [self convertSize: halfUnit  toView: nil];

    NSAffineTransform *transform = [NSAffineTransform transform];
    [transform translateXBy: 0.5
               yBy: 0.5];
    
    NSRect bounds = [self bounds];

    [NSGraphicsContext saveGraphicsState]; {
        [transform concat];
    
        
        [[NSColor whiteColor] set];
        [NSBezierPath fillRect: bounds];
        
        NSEnumerator *enumerator = [layers reverseLayerEnumerator];
        
        BWLayer *layer;
        while (layer = [enumerator nextObject]) {
            if (![layer visible]) {
                continue;
            }
            
            [layer drawRect: rect];
        }

    } [ NSGraphicsContext restoreGraphicsState];

    [[NSColor blackColor] set];
    [NSBezierPath strokeRect: bounds];
    
} // drawRect


- (BOOL) acceptsFirstResponder
{
    return (YES);
} // acceptsFirstResponder


// it just looks more natural when resizing
- (BOOL) isFlipped
{
    return (YES);
} // isFlipped


- (void) setZoom: (float) zoom
{
    // scale back to unit == 1.0 x 1.0
    NSSize unit = NSMakeSize (1.0, 1.0);
    unit = [self convertSize: unit  toView: nil];

    unit.width = 1.0 / unit.width;
    unit.height = 1.0 / unit.height;

    [self scaleUnitSquareToSize: unit];

    // now to get the zoom the user asks for
    NSSize zoomSize = NSMakeSize (zoom, zoom);

    [self scaleUnitSquareToSize: zoomSize];
    zoomFactor = zoom;

    [self adjustFrame];

    [self setNeedsDisplay: YES];
    [[self superview] setNeedsDisplay: YES];

} // setZoom


- (BWLayerSet *) layers
{
    return (layers);
} // layers


- (NSPoint) mouseEventPoint: (NSEvent *) event
{
    NSPoint mouse;
    mouse = [self convertPoint: [event locationInWindow]
                  fromView: nil];

    return (mouse);

} // convertMouse


- (void) mouseDown: (NSEvent *) event
{
    NSPoint mouse = [self mouseEventPoint: event];

    if ([currentTool handleMouseDown: event
                     atPoint: mouse]) {
    }
    
} // mouseDown


- (void) mouseDragged: (NSEvent *) event
{
    NSPoint mouse = [self mouseEventPoint: event];

    [currentTool handleMouseDragged: event
                 atPoint: mouse];

} // mouseDragged


- (void) mouseUp: (NSEvent *) event
{
    NSPoint mouse = [self mouseEventPoint: event];

    [currentTool handleMouseUp: event
                 atPoint: mouse];

} // mouseUp


- (void) keyDown: (NSEvent *) event
{
    NSPoint mouse = [self mouseEventPoint: event];

    [currentTool handleKeyDown: event
                 atPoint: mouse];
} // keyDown


- (void) keyUp: (NSEvent *) event
{
    NSPoint mouse = [self mouseEventPoint: event];

    [currentTool handleKeyUp: event
                 atPoint: mouse];
} // keyDown


- (void) flagsChanged: (NSEvent *) event
{
    // the point in the event isn't good, so backport it from the global
    // position
    NSPoint mouse = [NSEvent mouseLocation];
    mouse = [[self window] convertScreenToBase: mouse];
    mouse = [self convertPoint: mouse  fromView: nil];

    [currentTool handleFlagsChanged: event
                 atPoint: mouse];

} // flagsChanged


- (void) layerChangeNotification: (NSNotification *) notification
{
    [self setNeedsDisplay: YES];
} // layerChangeNotification


- (void) setLayers: (BWLayerSet *) l
{
    [l retain];
    [layers release];

    layers = l;

    NSNotificationCenter *center;
    center = [NSNotificationCenter defaultCenter];

    [center addObserver: self
            selector: @selector(layerChangeNotification:)
            name: BWLayer_ChangeNotification
            object: layers];

    [self adjustFrame];
    [self setNeedsDisplay: YES];

    [layers addObserver: self
            forKeyPath: @"setChange"
            options: nil
            context: NULL];

} // setLayers


- (void) observeValueForKeyPath: (NSString *) kp
                       ofObject: (id) object
                         change: (NSDictionary *) change
                        context: (void *) context
{
    if ([kp isEqualToString: @"setChange"]) {
        // this is causing extra redraws on mouse-down, but also
        // is vector for selection highlighting drawing, etc.
        // probably should use something else for that
        [self setNeedsDisplay: YES];
    }

    if ([kp isEqualToString: @"fabricChange"]) {
        [self adjustFrame];
        [self setNeedsDisplay: YES];
        [[self superview] setNeedsDisplay: YES];

        // !!! hack.  otherwise only the *previous* frame change
        // !!! takes effect
        [self performSelector: @selector(adjustFrame)
              withObject: self
              afterDelay: 0];
    }

} // observeValueForKeyPath


- (void) setTool: (BWTool *) newTool
{
    NSResponder *nextResponder;

    // this is the next element in the chain

    if (currentTool != nil) {
        nextResponder = [currentTool nextResponder];
    } else {
        nextResponder = [self nextResponder];
    }

    // decide who gets to point to the next responder

    if (newTool != nil) {
        // stick the tool into the chain
        [self setNextResponder: newTool];
        [newTool setNextResponder: nextResponder];

    } else {
        // cut the tool out of the chain (if there was one)
        [self setNextResponder: nextResponder];
    }

    [newTool retain];
    [currentTool release];
    
    currentTool = newTool;

} // setDrawTool


- (void) setFabric: (BWFabric *) f
{
    [fabric removeObserver: self
            forKeyPath: @"fabricChange"];
    
    [f retain];
    [fabric release];
    fabric = f;

    [f addObserver: self
       forKeyPath: @"fabricChange"
        options: NSKeyValueObservingOptionNew
        context: NULL];

} // setFabric


@end // BWLayerView
